package hypeerweb;

import java.io.Serializable;

/**
 * The State of a node in the HyPeerWeb.<br/>
 * <br/>
 *
 * <b>Domain:</b>
 * 		STATE_ID	: int
 * 
 * 
 * @author Ryan Hess
 */
public abstract class NodeState implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7264376098147323045L;
	/** The state id that each State returns.  Invalid for this general state. */
	public static final int STATE_ID = -1;
    
	/**
	 * Gets the node state assigned to the given id.
	 *
	 * @param id the State_ID to find the state for
	 * @pre 3 >= id >= 0 AND id != 1
	 * @post The NodeState assigned to the given id
	 */
	public static NodeState getNodeState(int id){
        if (id == 0) {
            return StandardNodeState.getSingleton();
        } else if (id == 1) {
            throw new UnsupportedOperationException("Invalid state id");
        } else if (id == 2) {
            return DownPointingNodeState.getSingleton();
        } else if (id == 3) {
            return HypercubeCapState.getSingleton();
        } 
        throw new UnsupportedOperationException("Invalid state id");
	}
    
	/**
	 * Finds the root node of the hypeerweb this node belongs to.
	 * @return the root Node, or the node with WebId = 0;
	 */
	private Node findRootNode(Node self) {
        Node smallestNode = self;
		while(smallestNode.getWebIdValue() != 0) {
			smallestNode = smallestNode.getSmallestNeighbor();
		}
		return smallestNode;
	}
	
	/**
	 * Starts at node zero and moves up to the deletion point
	 * 
	 * @arg Node rootNode - must equal the rootNode of this hypeerweb
	 * @pre rootNode argument must be the root node or node zero
	 * @post the node returned will be the deletion point
	 * @return the deletion point
	 */
	private Node ascendToDeletionPointFrom(Node rootNode) {
		Node anchorNode = rootNode;
		Node searchNode = anchorNode.getLargestNeighbor();
		
		while(searchNode != null  && searchNode.getWebIdValue() > anchorNode.getWebIdValue()) {
			anchorNode = searchNode;
			searchNode = anchorNode.getLargestNeighbor();
		}
		return anchorNode;
	}
    
    /**
     * 
     * @return the node in the hypeerweb with the highest numerical webID value
     */
    public Node findDeletionPoint(Node self) {
    	
    	// find root node
    	Node rootNode = findRootNode(self);
    	// from node 0, go to the deletion point
    	Node result = ascendToDeletionPointFrom(rootNode);    	
    	return result;
    }
    
    /**
     * Gets the insertion point from the current node.
     *
     * @param self the Node that this state is operating on
     * @pre self != null AND self exists in hypeerweb
     * @post the next node on the way to finding the insertion point
     * @post Returns self if self is the insertion point
     */
    public abstract Node getInsertionNode(Node self);
    
    /**
     * Finds the cap node from the current node.
     *
     * @param self the Node that this state is operating on
     * @pre self != null AND self exists in hypeerweb
     * @post the next node on the way to finding the cap node
     * @post Returns self if self is the cap node
     */
    public abstract Node getCapNode(Node self);
    
    /**
     * Gets the id of the current state.
     *
     * @pre <i>None</i>
     * @post the id of the node state
     */
    public abstract int getID();
    
    /**
     * Gets the node state that child of self should have.
     *
     * @param self the node the state is currently operating on
     * @pre self != null AND self exists in hypeerweb
     * @post the node state that self's child should have.
     */
    public abstract NodeState getChildNodeState(Node self);
    
    public abstract NodeState getParentNodeState(Node self);

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public abstract String toString();

}
